package com.liao.seckill.controller;

import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.liao.seckill.config.AccessLimit;
import com.liao.seckill.pojo.Order;
import com.liao.seckill.pojo.SeckillMessage;
import com.liao.seckill.pojo.SeckillOrder;
import com.liao.seckill.pojo.User;
import com.liao.seckill.rabbitmq.MQSenderMessage;
import com.liao.seckill.service.GoodsService;
import com.liao.seckill.service.OrderService;
import com.liao.seckill.service.SeckillOrderService;
import com.liao.seckill.vo.GoodsVo;
import com.liao.seckill.vo.RespBean;
import com.liao.seckill.vo.RespBeanEnum;
import com.ramostear.captcha.HappyCaptcha;
import com.ramostear.captcha.common.Fonts;
import com.ramostear.captcha.support.CaptchaStyle;
import com.ramostear.captcha.support.CaptchaType;
import org.apache.ibatis.annotations.Param;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.data.redis.core.script.RedisScript;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.net.URL;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import java.util.zip.Adler32;

/**
 * @author lzp
 * @version 1.0
 * lzpnb!
 */
@Controller
@RequestMapping("/seckill")
public class SeckillController implements InitializingBean {

    @Resource
    private GoodsService goodsService;

    @Resource
    private SeckillOrderService seckillOrderService;

    @Resource
    private OrderService orderService;

    @Resource
    private RedisTemplate redisTemplate;


    private HashMap<Long,Boolean> entryStockMap = new HashMap<>();

    @Resource
    private MQSenderMessage mqSenderMessage;

    @Resource
    private RedisScript<Long> script;

//    @RequestMapping("/doSeckill")
//    public String doSeckill(Model model, User user,Long goodsId) {
//        System.out.println("------秒杀v1.0--------");
//        if (user == null) {
//            return "login";
//        }
//        model.addAttribute("user",user);
//        GoodsVo goodsVo = goodsService.findGoodsVoByGoodsId(goodsId);
//        if (goodsVo.getStockCount() < 1) {
//            model.addAttribute("errmsg", RespBeanEnum.ENTRY_STOCK.getMessage());
//            return "seckillFail";
//        }
//
//        SeckillOrder seckillOrder = seckillOrderService.getOne(new QueryWrapper<SeckillOrder>().eq("user_id", user.getId()).eq("goods_id", goodsId));
//
//
//        if (seckillOrder != null) {
//            model.addAttribute("errmsg",RespBeanEnum.REPEAT_ERROR.getMessage());
//            return "seckillFail";
//        }
//
//        Order order = orderService.seckill(user, goodsVo);
//        if (order == null) {
//            model.addAttribute("errmsg", RespBeanEnum.ENTRY_STOCK.getMessage());
//            return "seckillFail";
//        }
//        model.addAttribute("order",order);
//        model.addAttribute("goods",goodsVo);
//        System.out.println("------秒杀v1.0--------");
//        return "orderDetail";
//    }


//    @RequestMapping("/doSeckill")
//    public String doSeckill(Model model, User user,Long goodsId) {
//        System.out.println("------秒杀v2.0--------");
//        if (user == null) {
//            return "login";
//        }
//        model.addAttribute("user",user);
//        GoodsVo goodsVo = goodsService.findGoodsVoByGoodsId(goodsId);
//        if (goodsVo.getStockCount() < 1) {
//            model.addAttribute("errmsg", RespBeanEnum.ENTRY_STOCK.getMessage());
//            return "seckillFail";
//        }
//
//
//
//        SeckillOrder o = (SeckillOrder) redisTemplate.opsForValue()
//                .get("order:" + user.getId() +":"+goodsVo.getId());
//        if (o != null) {
//            model.addAttribute("errmsg",
//                    RespBeanEnum.REPEAT_ERROR.getMessage());
//            return "secKillFail";
//        }
//
//
//        Order order = orderService.seckill(user, goodsVo);
//        if (order == null) {
//            model.addAttribute("errmsg", RespBeanEnum.ENTRY_STOCK.getMessage());
//            return "seckillFail";
//        }
//        model.addAttribute("order",order);
//        model.addAttribute("goods",goodsVo);
//        System.out.println("------秒杀v2.0--------");
//        return "orderDetail";
//    }


//    @RequestMapping("/doSeckill")
//    public String doSeckill(Model model, User user,Long goodsId) {
//        System.out.println("------秒杀v3.0--------");
//        if (user == null) {
//            return "login";
//        }
//        model.addAttribute("user",user);
//        GoodsVo goodsVo = goodsService.findGoodsVoByGoodsId(goodsId);
//        if (goodsVo.getStockCount() < 1) {
//            model.addAttribute("errmsg", RespBeanEnum.ENTRY_STOCK.getMessage());
//            return "seckillFail";
//        }
//
//
//
//        SeckillOrder o = (SeckillOrder) redisTemplate.opsForValue()
//                .get("order:" + user.getId() +":"+goodsVo.getId());
//        if (o != null) {
//            model.addAttribute("errmsg",
//                    RespBeanEnum.REPEAT_ERROR.getMessage());
//            return "secKillFail";
//        }
//
//        Long decrement = redisTemplate.opsForValue().decrement("seckillGoods:" + goodsId);
//        if (decrement < 0) {
//            redisTemplate.opsForValue().increment("seckillGoods:" + goodsId);
//            model.addAttribute("errmsg", RespBeanEnum.ENTRY_STOCK.getMessage());
//            return "seckillFail";
//        }
//
//        Order order = orderService.seckill(user, goodsVo);
//        if (order == null) {
//            model.addAttribute("errmsg", RespBeanEnum.ENTRY_STOCK.getMessage());
//            return "seckillFail";
//        }
//        model.addAttribute("order",order);
//        model.addAttribute("goods",goodsVo);
//        System.out.println("------秒杀v3.0--------");
//        return "orderDetail";
//    }

//    @RequestMapping("/doSeckill")
//    public String doSeckill(Model model, User user,Long goodsId) {
//        System.out.println("------秒杀v4.0--------");
//        if (user == null) {
//            return "login";
//        }
//        model.addAttribute("user",user);
//        GoodsVo goodsVo = goodsService.findGoodsVoByGoodsId(goodsId);
//        if (goodsVo.getStockCount() < 1) {
//            model.addAttribute("errmsg", RespBeanEnum.ENTRY_STOCK.getMessage());
//            return "seckillFail";
//        }
//
//
//
//        SeckillOrder o = (SeckillOrder) redisTemplate.opsForValue()
//                .get("order:" + user.getId() +":"+goodsVo.getId());
//        if (o != null) {
//            model.addAttribute("errmsg",
//                    RespBeanEnum.REPEAT_ERROR.getMessage());
//            return "secKillFail";
//        }
//
//        if(entryStockMap.get(goodsId)) {
//            model.addAttribute("errmsg", RespBeanEnum.ENTRY_STOCK.getMessage());
//            return "seckillFail";
//        }
//
//
//        Long decrement = redisTemplate.opsForValue().decrement("seckillGoods:" + goodsId);
//        if (decrement < 0) {
//            entryStockMap.put(goodsId,true);
//
//            redisTemplate.opsForValue().increment("seckillGoods:" + goodsId);
//            model.addAttribute("errmsg", RespBeanEnum.ENTRY_STOCK.getMessage());
//            return "seckillFail";
//        }
//
//        Order order = orderService.seckill(user, goodsVo);
//        if (order == null) {
//            model.addAttribute("errmsg", RespBeanEnum.ENTRY_STOCK.getMessage());
//            return "seckillFail";
//        }
//        model.addAttribute("order",order);
//        model.addAttribute("goods",goodsVo);
//        System.out.println("------秒杀v4.0--------");
//        return "orderDetail";
//    }

    @RequestMapping("/doSeckill")
    public String doSeckill(Model model, User user,Long goodsId) {
        System.out.println("------秒杀v5.0--------");
        if (user == null) {
            return "login";
        }
        model.addAttribute("user",user);
        GoodsVo goodsVo = goodsService.findGoodsVoByGoodsId(goodsId);
        if (goodsVo.getStockCount() < 1) {
            model.addAttribute("errmsg", RespBeanEnum.ENTRY_STOCK.getMessage());
            return "seckillFail";
        }



        SeckillOrder o = (SeckillOrder) redisTemplate.opsForValue()
                .get("order:" + user.getId() +":"+goodsVo.getId());
        if (o != null) {
            model.addAttribute("errmsg",
                    RespBeanEnum.REPEAT_ERROR.getMessage());
            return "secKillFail";
        }

        if(entryStockMap.get(goodsId)) {
            model.addAttribute("errmsg", RespBeanEnum.ENTRY_STOCK.getMessage());
            return "seckillFail";
        }


//        Long decrement = redisTemplate.opsForValue().decrement("seckillGoods:" + goodsId);
//        if (decrement < 0) {
//            entryStockMap.put(goodsId,true);
//
//            redisTemplate.opsForValue().increment("seckillGoods:" + goodsId);
//            model.addAttribute("errmsg", RespBeanEnum.ENTRY_STOCK.getMessage());
//            return "seckillFail";
//        }

        String uuid = UUID.randomUUID().toString();
        Boolean lock =
                redisTemplate.opsForValue().setIfAbsent("lock", uuid, 3, TimeUnit.SECONDS);

        if (lock) {
            //释放锁
            //定义 lua 脚本
//            String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
//// 使用 redis 执行 lua 执行
//            DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>();
//            redisScript.setScriptText(script);
//            redisScript.setResultType(Long.class);
            //执行你自己的业务
            Long decrement = redisTemplate.opsForValue().decrement("seckillGoods:" + goodsId);
            if (decrement < 0) {
            //这里使用内存标记, 避免多次操作 Redis, true 表示没有库存了
                entryStockMap.put(goodsId, true);
            //将库存恢复成 0
                redisTemplate.opsForValue().increment("seckillGoods:" + goodsId);
                redisTemplate.execute(script, Arrays.asList("lock"), uuid);
                model.addAttribute("errmsg", RespBeanEnum.ENTRY_STOCK.getMessage());
                return "secKillFail";//错误页面
            }
            redisTemplate.execute(script, Arrays.asList("lock"), uuid);
        } else {
            //3 获取锁失败、返回信息, 这次抢购失败,请继续抢购
            model.addAttribute("errmsg", RespBeanEnum.SEC_KILL_RETRY.getMessage());
            return "secKillFail";//错误页面
            }

        SeckillMessage seckillMessage = new SeckillMessage(user,goodsId);
        mqSenderMessage.sendSeckillMessage(JSONUtil.toJsonStr(seckillMessage));
        model.addAttribute("errmsg","排队中...");
        return "seckillFail";
    }

//    @RequestMapping("/{path}/doSeckill")
//    @ResponseBody
//    public RespBean doSeckill(@PathVariable String path, Model model, User user, Long goodsId) {
//        System.out.println("------秒杀v6.0--------");
//        if (user == null) {
//            return RespBean.error(RespBeanEnum.SESSION_ERROR);
//        }
//
//        boolean b = orderService.checkPath(user, goodsId, path);
//        if(!b) {
//            return RespBean.error(RespBeanEnum.REQUEST_ILLEGAL);
//        }
//
//
//        GoodsVo goodsVo = goodsService.findGoodsVoByGoodsId(goodsId);
//        if (goodsVo.getStockCount() < 1) {
//            model.addAttribute("errmsg", RespBeanEnum.ENTRY_STOCK.getMessage());
//            return RespBean.error(RespBeanEnum.ENTRY_STOCK);
//        }
//
//
//        SeckillOrder o = (SeckillOrder) redisTemplate.opsForValue()
//                .get("order:" + user.getId() +":"+goodsVo.getId());
//        if (o != null) {
//            return RespBean.error(RespBeanEnum.REPEAT_ERROR);
//        }
//
//        if(entryStockMap.get(goodsId)) {
//            return RespBean.error(RespBeanEnum.ENTRY_STOCK);
//        }
//
//
//        Long decrement = redisTemplate.opsForValue().decrement("seckillGoods:" + goodsId);
//        if (decrement < 0) {
//            entryStockMap.put(goodsId,true);
//
//            redisTemplate.opsForValue().increment("seckillGoods:" + goodsId);
//            return RespBean.error(RespBeanEnum.ENTRY_STOCK);
//        }
//        SeckillMessage seckillMessage = new SeckillMessage(user,goodsId);
//        mqSenderMessage.sendSeckillMessage(JSONUtil.toJsonStr(seckillMessage));
//        return RespBean.error(RespBeanEnum.SEK_KILL_WAIT);
//
//    }

    @RequestMapping("/path")
    @ResponseBody
    @AccessLimit(second = 5,maxCount = 5,needLogin = true)
    public RespBean getPath(User user,Long goodsId,String captcha,HttpServletRequest request) {
        if(user == null || goodsId < 0 || !StringUtils.hasText(captcha)) {
            return RespBean.error(RespBeanEnum.SESSION_ERROR);
        }


//        String uri = request.getRequestURI();
//        String key = uri + ":" + user.getId();
//        ValueOperations valueOperations = redisTemplate.opsForValue();
//        Integer count =(Integer) valueOperations.get(key);
//        if(count == null) {
//            valueOperations.set(key,1,5,TimeUnit.SECONDS);
//        }else if (count < 5) {
//            valueOperations.increment(key);
//        }else {
//            return RespBean.error(RespBeanEnum.ACCESS_LIMIT_REACHED);
//        }


        boolean check = orderService.checkCaptcha(user, goodsId, captcha);
        if(!check) {
            return RespBean.error(RespBeanEnum.CAPTCHA_ERROR);
        }
        String path = orderService.createPath(user, goodsId);
        return RespBean.success(path);
    }

    @RequestMapping("/captcha")
    public void happyCaptcha(HttpServletRequest request, HttpServletResponse response, User user ,Long goodsId) {
        HappyCaptcha.require(request, response)
                .style(CaptchaStyle.ANIM) //设置展现样式为动画
                .type(CaptchaType.NUMBER) //设置验证码内容为数字
                .length(6) //设置字符长度为6
                .width(220) //设置动画宽度为220
                .height(80) //设置动画高度为80
                .font(Fonts.getInstance().zhFont()) //设置汉字的字体
                .build().finish(); //生成并输出验证码

        redisTemplate.opsForValue().set("captcha:" + user.getId() + ":" + goodsId,(String) request.getSession().getAttribute("happy-captcha"),100, TimeUnit.SECONDS);

    }


    @Override
    public void afterPropertiesSet() throws Exception {
        List<GoodsVo> list = goodsService.findGoodsVo();
        if(CollectionUtils.isEmpty(list)){
            return;
        }

        list.forEach(goodsVo ->{

            redisTemplate.opsForValue().set("seckillGoods:" + goodsVo.getId(),goodsVo.getStockCount());
            entryStockMap.put(goodsVo.getId(),false);
        });

    }
}
